package executor

import (
	"context"
	"fmt"
	"log"
	"sync"
	"sync/atomic"
	"time"
	"xin-job/client/callback"
	"xin-job/client/handler"
	"xin-job/core/biz"
)

var instance = &JobExecutor{}

func GetInstance() *JobExecutor {
	return instance
}

type JobExecutor struct {
	handlerRepository sync.Map      // 任务仓库，用户缓存项目内所有的定时任务
	handlersCount     atomic.Uint32 // 任务个数
	runHandlers       sync.Map      // 执行中的任务 jobId,context
}

// runHandler 正在执行的任务
type runHandler struct {
	jobId  int // 任务ID
	ctx    context.Context
	cancel context.CancelFunc
}

// addHandler 新增任务
func (e *JobExecutor) AddHandler(h handler.Handler) {
	e.handlerRepository.Store(h.GetName(), h)
	e.handlersCount.Add(1)
}

// loadHandler 根据任务名称查询任务
func (e *JobExecutor) LoadHandler(name string) handler.Handler {
	h, ok := e.handlerRepository.Load(name)
	if ok {
		if v, ok := h.(handler.Handler); ok {
			return v
		}
	}
	return nil
}

// kill 杀死任务
func (e *JobExecutor) Kill(jobId int) error {
	runHandler := e.getRunHandler(jobId)
	if runHandler == nil {
		return fmt.Errorf("%d not run handler", jobId)
	}

	runHandler.cancel()
	return nil
}

// isRun 任务是否在执行
func (e *JobExecutor) IsRun(jobId int) bool {
	runHandler := e.getRunHandler(jobId)
	return runHandler != nil
}

func (e *JobExecutor) getRunHandler(jobId int) *runHandler {
	t, ok := e.runHandlers.Load(jobId)
	if ok {
		if v, ok := t.(*runHandler); ok {
			return v
		}
	}
	return nil
}

// runHandler 执行任务
func (e *JobExecutor) RunHandler(h handler.Handler, p biz.TriggerParam) {
	// 开启一个协程直接运行，不阻碍 http 结果返回到 admin
	var ctx context.Context
	var cancel context.CancelFunc

	if p.ExecutorTimeout == 0 {
		ctx, cancel = context.WithCancel(context.Background())
	} else {
		// 超时控制
		ctx, cancel = context.WithTimeout(context.Background(), time.Duration(p.ExecutorTimeout)*time.Millisecond)
	}
	//defer cancel()

	runHandler := &runHandler{
		jobId:  p.JobId,
		ctx:    ctx,
		cancel: cancel,
	}

	e.runHandlers.Store(p.JobId, runHandler)

	go func() {
		defer func() {
			if err := recover(); err != nil {
				msg := fmt.Sprintf("handle exec err, %s", err)
				callback.GetInstance().PushCallBack(&biz.HandleCallbackParam{
					LogId:      p.JobId,
					LogDateTim: p.LogDateTime,
					HandleCode: 500,
					HandleMsg:  msg,
				})
			}
		}()

		defer cancel()
		select {
		case <-ctx.Done():
			// 主动取消等情况
			handlerCode := 500
			handlerMsg := ctx.Err().Error()

			log.Printf("executor ctx done msg = %s\n", handlerMsg)

			// timeout
			if ctx.Err() == context.DeadlineExceeded {
				handlerCode = 502
				handlerMsg = "timeout"
			}

			callback.GetInstance().PushCallBack(&biz.HandleCallbackParam{
				LogId:      p.JobId,
				LogDateTim: p.LogDateTime,
				HandleCode: handlerCode,
				HandleMsg:  handlerMsg,
			})

		default:
			handleCode := 200
			handleMsg := "success"

			var err error
			err = h.Init()

			if err == nil {
				err = h.Execute()
				if err == nil {
					err = h.Destroy()
				}
			}

			if err != nil {
				handleCode = 500
				handleMsg = err.Error()
			}

			callbackParam := biz.HandleCallbackParam{
				LogId:      p.JobId,
				LogDateTim: p.LogDateTime,
				HandleCode: handleCode,
				HandleMsg:  handleMsg,
			}

			callback.GetInstance().PushCallBack(&callbackParam)
		}
	}()
}
